class Recorder {
    constructor(e = {}) {
        // 是否播放
        this.isplaying = false;
        // 左声道数据
        this.lBuffer = [];
        // 右声道数据
        this.rBuffer = [];
        // 临时PCM数据
        this.tempPCM = [];
        // 输入采样率
        this.inputSampleBits = 16;
        // 播放时间
        this.playStamp = 0;
        // 播放时间
        this.playTime = 0;
        // 总播放时间
        this.totalPlayTime = 0;
        // 偏移量
        this.offset = 0;
        // 文件大小
        this.fileSize = 0;

        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        this.inputSampleRate = audioContext.sampleRate;
        this.config = {
            sampleBits: ~[8, 16].indexOf(e.sampleBits) ? e.sampleBits : 16,
            sampleRate: ~[8000, 11025, 16000, 22050, 24000, 44100, 48000].indexOf(e.sampleRate) ? e.sampleRate : this.inputSampleRate,
            numChannels: ~[1, 2].indexOf(e.numChannels) ? e.numChannels : 1,
            compiling: !!e.compiling || false
        };
        this.outputSampleRate = this.config.sampleRate;
        this.oututSampleBits = this.config.sampleBits;
        this.littleEdian = (() => {
            const buffer = new ArrayBuffer(2);
            new DataView(buffer).setInt16(0, 256, true);
            return 256 === new Int16Array(buffer)[0];
        })();
        Recorder.initUserMedia();
        this.silentDuration = 0; // 静音时间统计
        this.onSilentDetected = null; // 静音检测回调
    }

    /**
    * 检查权限
    * 如果没有权限，抛出错误并禁用功能
    */
    static async getMicrophonePermission() {
        try {
            await navigator.mediaDevices.getUserMedia({ audio: true });
            return true
        } catch (error) {
            console.error("录音权限被拒绝或不支持！");
            throw new Error("无法访问音频设备。功能已禁用。");
            return false
        }
    }
     /**
     * 初始化录音器
     */
    initRecorder() {
        if (this.context) this.destroy();
        this.context = new (window.AudioContext || window.webkitAudioContext)();
        this.analyser = this.context.createAnalyser();
        this.analyser.fftSize = 2048;
        const createScriptProcessor = this.context.createScriptProcessor || this.context.createJavaScriptNode;
        this.recorder = createScriptProcessor.apply(this.context, [4096, this.config.numChannels, this.config.numChannels]);
        this.recorder.onaudioprocess = (e) => {
            if (this.isrecording && !this.ispause) {
                const leftChannelData = e.inputBuffer.getChannelData(0);

                let rightChannelData = null;
                this.lBuffer.push(new Float32Array(leftChannelData));
                this.size += leftChannelData.length;
                if (this.config.numChannels === 2) {
                    rightChannelData = e.inputBuffer.getChannelData(1);
                    this.rBuffer.push(new Float32Array(rightChannelData));
                    this.size += rightChannelData.length;
                }
                if (this.config.compiling) {
                    const pcmData = this.transformIntoPCM(leftChannelData, rightChannelData);
                    this.tempPCM.push(pcmData);
                    this.fileSize = pcmData.byteLength * this.tempPCM.length;
                } else {
                    this.fileSize = Math.floor(this.size / Math.max(this.inputSampleRate / this.outputSampleRate, 1)) * (this.oututSampleBits / 8);
                }
                const volume = 100 * Math.max.apply(Math, leftChannelData);
                this.duration += 4096 / this.inputSampleRate;
                if (this.onprocess) this.onprocess(this.duration);
                if (this.onprogress) this.onprogress({
                    duration: this.duration,
                    fileSize: this.fileSize,
                    vol: volume,
                    data: this.tempPCM
                });
            }
        };
    }

    /**
     * 开始录音
     */
    async start() {
        await Recorder.getMicrophonePermission();
        if (!this.isrecording) {
            this.clear();
            this.initRecorder();
            this.isrecording = true;
            return navigator.mediaDevices.getUserMedia({ audio: true })
                .then((stream) => {
                    this.audioInput = this.context.createMediaStreamSource(stream);
                    this.stream = stream;
                })
                .then(() => {
                    this.audioInput.connect(this.analyser);
                    this.analyser.connect(this.recorder);
                    this.recorder.connect(this.context.destination);
                });
        }
    }
    /**
     * 停止录音
     */
    stop() {
        this.isrecording = false;
        if (this.audioInput) this.audioInput.disconnect();
        this.recorder?.disconnect();
    }

    /**
     * 转换录音数据为 PCM 格式
     * @returns {ArrayBuffer} PCM 格式的音频数据
     */
    toPCM() {
        const flatData = this.flat();
        const compressedData = Recorder.compress(flatData, this.inputSampleRate, this.outputSampleRate);
        return Recorder.encodePCM(compressedData, this.oututSampleBits, this.littleEdian);
    }
    

    /**
     * 获取下一段数据
     * @returns 
     */
    getNextData() {        
        const chunkSize = 1280;
        const currentOffset = this.tempPCM.length;
        const dataSlice = this.tempPCM.slice(this.offset);
        
        if (dataSlice.length) {
            const buffer = new ArrayBuffer(dataSlice.length * dataSlice[0].byteLength);
            const dataView = new DataView(buffer);
            let offset = 0;
            dataSlice.forEach((chunk) => {
                for (let i = 0, len = chunk.byteLength; i < len; ++i) {
                    dataView.setInt8(offset, chunk.getInt8(i));
                    offset++;
                }
            });
            this.PCM = dataView;
            this.tempPCM = [];
        }

        if (this.PCM) {
            const pcmBuffer = this.PCM.buffer;
            const pcmLength = pcmBuffer.byteLength;
            const chunk = pcmBuffer.slice(0, Math.min(chunkSize, pcmLength));
            this.PCM = new DataView(pcmBuffer.slice(chunkSize)); // 更新PCM为剩余数据
            return this.arrayBufferToBase64(chunk);
        }

        const compressedData = Recorder.compress(flatData, this.inputSampleRate, this.outputSampleRate);
        this.PCM = Recorder.encodePCM(compressedData, this.oututSampleBits, this.littleEdian);
        this.offset = currentOffset;
        if (blob) {
            return new Blob([compressedData]);
        }
        const compressedBuffer = compressedData.buffer;
        const chunk = compressedBuffer.slice(0, Math.min(chunkSize, compressedBuffer.byteLength));
        this.PCM = new DataView(compressedBuffer.slice(chunkSize)); // 更新PCM为剩余数据
        return this.arrayBufferToBase64(chunk);
    }

    // 将ArrayBuffer转换为Base64
    arrayBufferToBase64(buffer) {
        let binary = '';
        const bytes = new Uint8Array(buffer);
        const len = bytes.byteLength;
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return btoa(binary);
    }
    // Base64转换为ArrayBuffer
    base64ToArrayBuffer(base64) {
        const binaryString = atob(base64);
        const len = binaryString.length;
        const bytes = new Uint8Array(len);
        for (let i = 0; i < len; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        return bytes.buffer;
    }

    // 获取PCM数据
    getPCM() {
        // 检查是否有临时PCM数据
        if (this.tempPCM.length) {
            // 为临时PCM数据的总长度创建一个新的ArrayBuffer
            const buffer = new ArrayBuffer(this.tempPCM.length * this.tempPCM[0].byteLength);
            const dataView = new DataView(buffer);
            let offset = 0;
            // 遍历每个PCM数据块
            this.tempPCM.forEach((chunk) => {
                // 将每个数据块的字节数据复制到新的DataView中
                for (let i = 0, len = chunk.byteLength; i < len; ++i) {
                    dataView.setInt8(offset, chunk.getInt8(i));
                    offset++;
                }
            });            
            // 将合并后的DataView赋值给PCM属性
            this.PCM = dataView;
            // 清空临时PCM数据
            this.tempPCM = [];
        }
        // 如果PCM数据存在，直接返回
        if (this.PCM) {
            return this.PCM;
        }

        // 否则，扁平化数据
        const flatData = this.flat();
        // 压缩数据
        const compressedData = Recorder.compress(flatData, this.inputSampleRate, this.outputSampleRate);
        // 编码为PCM格式
        this.PCM = Recorder.encodePCM(compressedData, this.oututSampleBits, this.littleEdian);
        // 返回PCM数据
        return this.PCM;   
    }

    // 转换为PCM格式
    transformIntoPCM(leftChannelData, rightChannelData) {
        const left = new Float32Array(leftChannelData);
        const right = new Float32Array(rightChannelData);
        const compressedData = Recorder.compress({ left, right }, this.inputSampleRate, this.outputSampleRate);
        const encodedPCM = Recorder.encodePCM(compressedData, this.oututSampleBits, this.littleEdian);
        return encodedPCM;
    }

    // 销毁录音机
    destroy() {
        this.stopStream();
        return this.closeAudioContext();
    }

    // 停止流
    stopStream() {
        if (this.stream && this.stream.getTracks) {
            this.stream.getTracks().forEach((track) => track.stop());
            this.stream = null;
        }
    }

    // 关闭音频上下文
    closeAudioContext() {
        if (this.context && this.context.close && this.context.state !== "closed") {
            return this.context.close();
        }
        return Promise.resolve();
    }


    
    // 清除数据
    clear() {
        this.lBuffer.length = 0;
        this.rBuffer.length = 0;
        this.size = 0;
        this.fileSize = 0;
        this.PCM = null;
        this.audioInput = null;
        this.duration = 0;
        this.ispause = false;
        if (this.source) {
            this.source.stop();
            this.source = null;
        }
    }

    // 扁平化数据
    flat() {
        let leftChannel = null;
        let rightChannel = new Float32Array(0);
        if (this.config.numChannels === 1) {
            leftChannel = new Float32Array(this.size);
        } else {
            leftChannel = new Float32Array(this.size / 2);
            rightChannel = new Float32Array(this.size / 2);
        }
        let offset = 0;
        for (let i = 0; i < this.lBuffer.length; i++) {
            leftChannel.set(this.lBuffer[i], offset);
            offset += this.lBuffer[i].length;
        }
        offset = 0;
        for (let i = 0; i < this.rBuffer.length; i++) {
            rightChannel.set(this.rBuffer[i], offset);
            offset += this.rBuffer[i].length;
        }
        return { left: leftChannel, right: rightChannel };
    }


    // 压缩数据
    static compress(data, inputSampleRate, outputSampleRate) {
        const compression = inputSampleRate / outputSampleRate;
        const maxCompression = Math.max(compression, 1);
        const left = data.left;
        const right = data.right;
        const length = Math.floor((left.length + right.length) / compression);
        const result = new Float32Array(length);
        let index = 0;
        let offset = 0;
        while (index < length) {
            const position = Math.floor(offset);
            result[index] = left[position];
            index++;
            if (right.length) {
                result[index] = right[position];
                index++;
            }
            offset += maxCompression;
        }
        return result;
    }

    // 编码PCM
    static encodePCM(data, sampleBits, littleEndian = true) {
        let offset = 0;
        const bufferLength = data.length * (sampleBits / 8);
        const buffer = new ArrayBuffer(bufferLength);
        const dataView = new DataView(buffer);
        if (sampleBits === 8) {
            for (let i = 0; i < data.length; i++, offset++) {
                const s = Math.max(-1, Math.min(1, data[i]));
                const val = s < 0 ? 128 * s : 127 * s;
                dataView.setInt8(offset, val + 128);
            }
        } else {
            for (let i = 0; i < data.length; i++, offset += 2) {
                const s = Math.max(-1, Math.min(1, data[i]));
                dataView.setInt16(offset, s < 0 ? 32768 * s : 32767 * s, littleEndian);
            }
        }
        return dataView;
    }


    // 抛出错误
    static throwError(error) {
        throw new Error(error);
    }

    // 初始化用户媒体
    static initUserMedia() {
        if (navigator.mediaDevices === undefined) {
            navigator.mediaDevices = {};
        }
        if (navigator.mediaDevices.getUserMedia === undefined) {
            navigator.mediaDevices.getUserMedia = function (constraints) {
                const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
                if (!getUserMedia) {
                    return Promise.reject(new Error("浏览器不支持 getUserMedia !"));
                }
                return new Promise((resolve, reject) => {
                    getUserMedia.call(navigator, constraints, resolve, reject);
                });
            };
        }
    }

    // 获取权限
    static getPermission() {
        this.initUserMedia();
        return navigator.mediaDevices.getUserMedia({ audio: true })
            .then((stream) => {
                stream.getTracks().forEach((track) => track.stop());
            });
    }
}

export default Recorder;